跳到主要内容

K6 测试工具

k6 是什么?

k6 是用 Go 语言编写的一种高性能的负载测试工具。具有下面几个特点。

  • K6 嵌入了 JavaScript 运行时,可以使用 JavaScript ES2015/ES6 来编写脚本。
  • 强大的 CLI 工具。
  • 使用 Checks 和 Thresholds 可以更加轻松的做面向目标的自动化的负载测试。

配置环境

Debian/Ubuntu

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install k6

如果使用 Docker 安装

docker pull grafana/k6

其它安装方式看 官网

如何使用 K6

创建一个 single-request.js 文件

import http from 'k6/http';
import { sleep } from 'k6';

export default function () {
http.get('https://test.k6.io');
sleep(1);
}

运行这个用例:

# 直接使用 
k6 run single-request.js

# 使用 docker,这个 - 是 docker 用来接受标准输入的参数,后面那个 < 表示通过管道把 single-request.js 传入进去
docker run -i grafana/k6 run - <single-request.js

# 或者增加点压测,vus 表示虚拟用户数量,duration 表示持续时间
# 这个 vus 实际上是并行的 while (true) 循环
k6 run --vus 10 --duration 30s single-request.js

脚本至少必须包含一个 default function。这个函数定义了 VUs 的入口点,类似于许多其他语言中的 main 函数

注意,这个 default function 和 main 函数还是有区别的,它会在上面提到的 while (true) 循环重复的执行,但是其它地方的代码只会被执行一次

整个结构如下:

20220422112904

使用 options

上面设置虚拟用户和持续时间是通过命令来指定的,但是每次都需要用户自己指定参数太麻烦了,所以这时可以使用 options 直接指定到脚本上面

import http from 'k6/http';
import { sleep } from 'k6';

export const options = {
vus: 10,
duration: '30s',
};

export default function () {
http.get('http://test.k6.io');
sleep(1);
}

这样直接运行就行了

k6 run script.js

可以加一个 stages 用来模拟瞬时高峰的情况,例如将负载测试配置为在一天的大部分时间内保持60个用户,并在运行的高峰时间内增加到100个用户,然后再减少到正常负载。具体参考官网的 Load testing

export const options = {
stages: [
{ duration: '5m', target: 60 }, // simulate ramp-up of traffic from 1 to 60 users over 5 minutes.
{ duration: '10m', target: 60 }, // stay at 60 users for 10 minutes
{ duration: '3m', target: 100 }, // ramp-up to 100 users over 3 minutes (peak hour starts)
{ duration: '2m', target: 100 }, // stay at 100 users for short amount of time (peak hour)
{ duration: '3m', target: 60 }, // ramp-down to 60 users over 3 minutes (peak hour ends)
{ duration: '10m', target: 60 }, // continue at 60 for additional 10 minutes
{ duration: '5m', target: 0 }, // ramp-down to 0 users
],
thresholds: {
http_req_duration: ['p(99)<1500'], // 99% of requests must complete below 1.5s
},
};

20220422113407

断言 Checks

一个简单的 HTTP 断言

import { check } from 'k6';
import http from 'k6/http';

export default function () {
const res = http.get('http://test.k6.io/');
check(res, {
'is status 200': (r) => r.status === 200,
'body size is 11,105 bytes': (r) => r.body.length == 11105,
'verify homepage text': (r) =>
r.body.includes('Collection of simple web-pages suitable for load testing'),
});
}

细节参考官网 Checks

期望 Thresholds

上面的期望是针对单个请求的,如果希望宏观的检查可以使用 Thresholds

import http from 'k6/http';

export const options = {
thresholds: {
http_req_failed: ['rate<0.01'], // http errors should be less than 1%
http_req_duration: ['p(95)<200'], // 95% of requests should be below 200ms
},
};

export default function () {
http.get('https://test-api.k6.io/public/crocodiles/1/');
}

HTTP 请求

具体使用参考官网 HTTP Requests

import http from 'k6/http';
export let options = {
vus: 100, // 指定要同时运行的虚拟用户数量
duration: '10s', // 指定测试运行的总持续时间
};
// default 默认函数
export default function () {
// 标头
let params = { headers: { 'Content-Type': 'application/json' } };

var res=http.get("https://test.k6.io",params)
}

gRPC 请求

具体使用参考官网 k6/net/grpc

import grpc from 'k6/net/grpc';
import { check, sleep } from 'k6';

const client = new grpc.Client();
client.load(['definitions'], 'hello.proto');

export default () => {
client.connect('grpcb.in:9001', {
// plaintext: false
});

const data = { greeting: 'Bert' };
const response = client.invoke('hello.HelloService/SayHello', data);

check(response, {
'status is OK': (r) => r && r.status === grpc.StatusOK,
});

console.log(JSON.stringify(response.message));

client.close();
sleep(1);
};

这个加载 proto 可以直接通过读取 json 的方式加载配置

{
"protoPaths": [
"../../",
"../../proto/ext"
],
"protoFiles": [
"activity.proto",
"order.proto"
]
}

然后读取时:

export const globalConfig = JSON.parse(open("k6.cfg.json"));
export const grpcClient = new grpc.Client();

grpcClient.load(globalConfig['protoPaths'], ...globalConfig['protoFiles']);

Mock 数据环境

一般需要在执行某个测试前初始化一些数据,这时可以使用自带的客户端

参考 Load Testing SQL Databases with k6

需要先安装这个 xk6-sql

# Install xk6:
go install go.k6.io/xk6/cmd/xk6@latest
# Build the binary:
xk6 build master \
--with github.com/grafana/xk6-sql

编写脚本

// script.js
import sql from 'k6/x/sql';

const db = sql.open("sqlite3", "./test.db");

export function setup() {
db.exec(`CREATE TABLE IF NOT EXISTS keyvalues (
id integer PRIMARY KEY AUTOINCREMENT,
key varchar NOT NULL,
value varchar);`);
}

export function teardown() {
db.close();
}

export default function () {
db.exec("INSERT INTO keyvalues (key, value) VALUES('plugin-name', 'k6-plugin-sql');");

let results = sql.query(db, "SELECT * FROM keyvalues;");
for (const row of results) {
console.log(`key: ${row.key}, value: ${row.value}`);
}
}

References